package com.have.haveblog.security.component;



import com.have.blog.base.constant.BaseSysConstant;
import com.have.blog.base.constant.BlogConstant;
import com.have.blog.base.holder.RequestHolder;
import com.have.blog.commons.config.jwt.Audience;
import com.have.blog.commons.service.RedisService;
import com.have.haveblog.security.utils.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Base64;
import java.util.Enumeration;

@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter{

    @Value("${jwt.tokenHeader}")
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private Audience audience;
    @Autowired
    private UserDetailsService userDetailsService;
    @Value("${spring.security.user.name}")
    private String name;
    @Value("${spring.security.user.password}")
    private String password;
    @Autowired
    private RedisService redisService;
    @Value("${redis.admin.database}")
    private String REDIS_KEY_DATABASE;
    private static final String BASIC = "BASIC ";

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
        String authHeader = request.getHeader(this.tokenHeader);
        if (authHeader.startsWith("Basic ")) { // 如果是微服务之间传递
            // 验证账号密码
            String pattern = name + ":" + password;
            String substring = authHeader.substring(BASIC.length());
            byte[] decode = Base64.getDecoder().decode(substring);
            if (pattern.equals(new String(decode))) {
                authHeader = request.getHeader("token");
            } else {
                authHeader = null;
            }
        }
        if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
            String authToken = authHeader.substring(this.tokenHead.length());
            // 检查token是否合法与刷新token
            String key = REDIS_KEY_DATABASE + ":"
                    + "LOGIN_ADMIN_KEY" + BlogConstant.SYMBOL_COLON
                    + authToken;
            Object obj = redisService.get(key);
            if (obj != null) {
                String username = jwtTokenUtil.getUsername(authToken, audience.getBase64Secret());
                log.info("checking username:{}", username);
                if (!StringUtils.isEmpty(username) && SecurityContextHolder.getContext().getAuthentication() == null) {
                    UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                    log.info("authentication resource:{}", userDetails.getAuthorities());
                    if (jwtTokenUtil.validateToken(authToken, userDetails, audience.getBase64Secret())) {
                        request.setAttribute(BaseSysConstant.TOKEN, authToken); // 将token加入request中，让后续的方法能够获取到token
                        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        log.info("authentication user:{}", username);
                        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                    }
                }
            }
        }
        chain.doFilter(request, response);
    }
}
